
import java.io.IOException;
import java.sql.SQLException;
import java.util.Scanner;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import javax.swing.UIManager;



/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Laurens
 */
public class Rename_Parameter_Refactor implements RefactoringMethod 
{
    /*
     * Initialization and declaration of variables
     */
    String location = " ";
    String oldParameter = " ";
    String newParameter = " ";
    referenceFinder find = new referenceFinder();
    characterList characters = new characterList();
    characterEditor characterEditor = new characterEditor();
    replaceValue replace_value = new replaceValue();
    NumberList numbers = new NumberList();
    FileEditor fileE = new FileEditor();
    ErrorMessages error = new ErrorMessages();
    int methodLineNumber = 0;
    int endOfMethod = 0;
    Stack<String> stack = new Stack<String>();
    /*
     * End of initialization and declaration of variables
     */
    
    /**
     * Default constructor, DOES NOTHING
     */
    public Rename_Parameter_Refactor()
    {
        //DOES NOTHING
    }
    
    /**
     * Does the rename refactoring, where is changes a old parameter with a new one.
     * @param parameters, String = [S], [S] path,[S] oldParameter, [S] newParameter
     * @return
     */
    @Override
    public Vector<String> refactor(Vector<String> parameters)
    {
        /*
         * Calls all the parameters needed to complete rename refactor
         */
        try 
        {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } 
        catch (Exception ex) 
        {
            Logger.getLogger(Rename_Parameter_Refactor.class.getName()).log(Level.SEVERE, null, ex);
        } 
        String path = "";
        String className = "";
        Vector<String> result = new Vector<String>();
        Vector<String> parameterList = new Vector<String>();
        Vector<String> methodList = new Vector<String>();
        String file = "";
        String method = "";
        if(parameters.size() < 0)
        {
            result.add(error.getErrorMessages("pathNotFound"));
            return result;
        }
        path = parameters.get(0);
        StringTokenizer stClassName = new StringTokenizer(path, "/");
        while(stClassName.hasMoreTokens())
        {
            className = stClassName.nextToken();
        }
        result.add(path);
        StringTokenizer classST = new StringTokenizer(className,".");
        String finalClassName = classST.nextToken(); 
        Database db = new Database();
        try
        {
            db.connect();
            db.initializeDB();
            ParserProxy parser = new ParserProxy("FileParser");
            parser.parseString(path);
            methodList = db.getMethod(finalClassName);
            //parameterList = db.getParamater(finalClassName);
        }
        catch(Exception e)
        {
            
        }
        if(parameters.get(1) == null)
        {
            Object[] selectionValues = new Object[methodList.size()];
            for(int i = 0; i < methodList.size(); i++)
            {
                selectionValues[i] = methodList.get(i);
            }
            method = JOptionPane.showInputDialog(null, "Please select the method?", "Method Selection", JOptionPane.QUESTION_MESSAGE, null, selectionValues,"").toString();
            methodLineNumber = db.getMethodLine(method, finalClassName);
            parameterList = db.getMethodParamater(method, finalClassName);
            if(parameterList.isEmpty())
            {
                result.add("Error, no parameters available in that method!");
                return result;
            }
            if(parameterList.size() < 20)
            {
                Object[] selectionValues2 = new Object[parameterList.size()];
                for(int i = 0; i < parameterList.size(); i++)
                {
                    selectionValues2[i] = parameterList.get(i);
                }
                oldParameter = JOptionPane.showInputDialog(null, "Please select the parameter?", "Parameter Selection", JOptionPane.QUESTION_MESSAGE, null, selectionValues2,"").toString();
                newParameter = JOptionPane.showInputDialog("Enter the new parameter name");
            }
            else
            {
                oldParameter = JOptionPane.showInputDialog("Enter old parameter");
                newParameter = JOptionPane.showInputDialog("Enter the new parameter name");
            }
        }
        else
        {
            oldParameter = parameters.get(1);
            newParameter = parameters.get(2);
        }
        boolean parameterClash = false;
        boolean parameterClash2 = false;
        Vector<String> globalVariables = new Vector<String>();
        boolean global = false;
        if(oldParameter.contains("$"))
        {
            result.add("Error, unable to do the refactoring because $ is not accepted in this program.\nPlease choose a different parameter.");
            return result;
        }
        if(newParameter.contains("$"))
        {
            result.add("Error, unable to do the refactoring because $ is not accepted in this program.\nPlease choose a different parameter.");
            return result;
        }
        try
        {
            /*
             * Reads the file needed to rename refactor
             */
            file = fileE.read(path);
            endOfMethod = getEndOfMethod(file);
        }
        catch(Exception e)
        {

        }
        try 
        {
            db.disconnect();
        } 
        catch (SQLException ex) 
        {
            Logger.getLogger(Rename_Parameter_Refactor.class.getName()).log(Level.SEVERE, null, ex);
        }
        try
        {
            RenameParameterAccess connect = new RenameParameterAccess();
            //Determines if there is a parameter clash (Then no refactoring possible)
            parameterClash = searchNewValue(newParameter, file);
            parameterClash2 = searchNewValue_Only_In_Method(newParameter, file);
            //Determines what global variables there are
            globalVariables = connect.globalVariables(finalClassName);
            for(int q = 0; q < globalVariables.size(); q++)
            {
                if(newParameter == null ? globalVariables.get(q) == null : newParameter.equals(globalVariables.get(q)))
                {
                    global = true;
                    q = globalVariables.size();
                }
            }
            boolean keywordsNew = characters.checkKeywords(newParameter);
            boolean punctuation = characters.punctuationCheck(newParameter);
            if(punctuation)
            {
                result.add(error.getErrorMessages("punctuation"));
                return result;
            }
            else if(keywordsNew)
            {
                result.add(error.getErrorMessages("keyword"));
                return result;
            }
            //if global is true then you may replace it, while changing the name binding condition
            else if(global)
            {
                result.add(conflict(replace(lineNumbersMethod(oldParameter, file),oldParameter, newParameter, file), oldParameter, newParameter));
            }
            //if there is a clash of parameters then you will not be able to refactor
            else if(!parameterClash)
            {
                result.add(conflict(replace(lineNumbersMethod(oldParameter, file),oldParameter, newParameter, file), oldParameter, newParameter));
            }
            else
            {
                if(!parameterClash2)
                {
                    result.add(conflict(replace(lineNumbersMethod(oldParameter, file),oldParameter, newParameter, file), oldParameter, newParameter));
                }
                else
                {
                    result.add(error.getErrorMessages("alreadyUsed"));
                    return result;
                }
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();
            result.add(error.getErrorMessages("tryFailed"));
            return result;
        }
        return result;
    }
    
    /**
     * This function replaces the oldParameter with the newParameter, where the line numbers correspond to lineNumbers<String>
     * and is situated in the text field file
     * @param lineNumbers, line numbers where oldParameter is found
     * @param oldParameter, parameter to be changed
     * @param newParameter, new parameter to be inserted
     * @param file, holds the file currently working with
     * @throws IOException
     */
    private Vector<String> replace(Vector<Integer> lineNumbers, String oldParameter, String newParameter, String file) throws IOException
    {
        Vector<Integer> sortedLineNumbers = new Vector<Integer>();
        if(lineNumbers == null)
        {
            return null;
        }
        //Sorting the lineNumbers in desceding order
        sortedLineNumbers = characterEditor.insertion_sort(lineNumbers);
        Vector<Integer> finalSortedLineNumbers = new Vector<Integer>();
        finalSortedLineNumbers = characterEditor.removeDuplicates(sortedLineNumbers);
        Scanner scanner = new Scanner(file);
        Vector<String> alphabet = characters.alphabet();
        int lineCounter = 0;
        Vector<String> result = new Vector<String>();
        int i = 1;
        //Scans through the file to search each line
        while(scanner.hasNext())
        {
            if(i == finalSortedLineNumbers.get(lineCounter))
            {
                String currentLine = scanner.nextLine();
                boolean letterFound = false;
                for(int y = 0; y < alphabet.size(); y++)
                {
                    //Checks to make sure there is no alphabet letter before the oldParameter
                    if(currentLine.contains(alphabet.get(y) + oldParameter))
                    {
                        result.add(replace_value.dealWithExtras(currentLine, oldParameter, newParameter));
                        letterFound = true;
                        y = alphabet.size() + 1;
                    }
                    else if(currentLine.contains(oldParameter + alphabet.get(y)))
                    {
                        result.add(replace_value.dealWithExtras(currentLine, oldParameter, newParameter));
                        letterFound = true;
                        y = alphabet.size() + 1;
                    }
                }
                //If no alphabet is found then you may replace the parameter
                if((letterFound == false))
                {
                    result.add(currentLine.replaceAll(oldParameter, newParameter));
                }
                if(lineCounter < finalSortedLineNumbers.size())
                {
                    if (lineCounter == finalSortedLineNumbers.size() - 1)
                    {
                    }
                    else
                    {
                        lineCounter++;
                    }
                }
            }
            else
            {
                result.add(scanner.nextLine());
            }
            i++;
        }
        //return the result vector with the partially refactored file
        return result;
    }

    /**
     * Determines the various line numbers where oldParameter is found
     * @param lineNumbers
     * @param oldParameter
     * @param file
     * @return
     */
    public Vector<Integer> lineNumbersMethod(String oldParameter, String file)
    {
        if(file.length() == 0)
        {
            return null;
        }
        else
        {
            int i = 1;
            Vector<String> characterList = characters.characterListMethod();
            Vector<Integer> result = new Vector<Integer>();
            Scanner scanner = new Scanner(file);
            //Scans the file to determine the line numbers of occurences of oldParameter
            while(scanner.hasNext())
            {
                String currentLine = scanner.nextLine();
                if(i == methodLineNumber - 1)
                {
                    for(int l = 0; l < characterList.size(); l++)
                    {
                        for(int q = 0; q < characterList.size(); q++)
                        {
                            //Checks if the parameter is found with certain characters before and after oldParameter
                            if(currentLine.contains(characterList.get(l) + oldParameter + characterList.get(q)))
                            {
                                result.add(i);
                            }
                            else
                            {
                                //NOTHING
                            }
                        }
                    }
                }
                else if(i >= methodLineNumber)
                {
                    if(i <= endOfMethod)
                    {
                        for(int l = 0; l < characterList.size(); l++)
                        {
                            for(int q = 0; q < characterList.size(); q++)
                            {
                                //Checks if the parameter is found with certain characters before and after oldParameter
                                if(currentLine.contains(characterList.get(l) + oldParameter + characterList.get(q)))
                                {
                                    result.add(i);
                                }
                                else
                                {
                                    //NOTHING
                                }
                            }
                        }
                    }
                }
                i++;
            }
            //returns the unsorted line numbers of occurences of oldParameter
            return result;
        }
    }

    /**
     * This function reads the file and changes occurences of global conflict
     * @param result
     * @param oldParameter
     * @param newParameter
     * @return
     */
    public String conflict(Vector<String> result, String oldParameter, String newParameter)
    {
        Vector<String> finalVector = new Vector<String>();
        Vector<String> spaceVector = new Vector<String>();
        spaceVector.add("");
        spaceVector.add(" ");
        spaceVector.add("  ");
        spaceVector.add("   ");
        spaceVector.add("    ");
        spaceVector.add("     ");
        spaceVector.add("      ");
        spaceVector.add("       ");
        spaceVector.add("        ");
        spaceVector.add("         ");
        spaceVector.add("          ");
        spaceVector.add("           ");
        spaceVector.add("            ");
        spaceVector.add("             ");
        spaceVector.add("              ");
        spaceVector.add("               ");
        spaceVector.add("                ");
        spaceVector.add("                 ");
        boolean found = false;
        if(result == null)
        {
            return null;
        }
        for(int i = 0; i < result.size(); i++)
        {
            //Searches the file for newParameter = newParameter, which then changes it to this.newParameter    
            for(int j = 0; j < spaceVector.size(); j++)
            {
                for(int k = 0; k < spaceVector.size(); k++)
                {
                    if(result.get(i).contains(newParameter + spaceVector.get(j) + "=" + spaceVector.get(k) + newParameter))
                    {
                        finalVector.add(result.get(i).replace(newParameter + spaceVector.get(j) + "=" + spaceVector.get(k) + newParameter, "this." + newParameter + " = " + newParameter));
                        found = true;
                    }
                }
            }
            if(!found)
            {
                finalVector.add(result.get(i));
            }
            found = false;
        }
        String output = "";
        for(int j = 0; j < finalVector.size(); j++)
        {
            output += finalVector.get(j) + "\n";
        }
        //returns final refactored string
        return output;
    }
    
    /**
     * Returns end line of the current method
     * @param file
     * @return 
     */
    public int getEndOfMethod(String file)
    {
        Scanner st = new Scanner(file);
        String current ="";
        boolean fb = true;
        boolean bc = true;
        boolean quoteFlag = true;
        int counter = 0;
        int lineCount = 1;
        while(st.hasNext())
        {
            current = st.nextLine();
            
            if(current.length() >= 2 && current.trim().length() >= 2 && current.trim().substring(0, 2).equals("//"))
            {}
            else
            {
                if(lineCount >= methodLineNumber)
                {
                for(int i = 0; i < current.length() ;i++)
                {
                    if(current.length() >= i+2 && current.substring(i,i+2).equals("/*") && fb == true && quoteFlag == true)
                    {
                        fb = false;
                        quoteFlag = false;
                        bc = false;
                    }


                    if(current.length() >= i+2 && current.substring(i,i+2).equals("*/"))
                    {
                        fb = true;
                        quoteFlag = true;
                        bc = true;
                        i++;
                        i++;
                    }
                    
                    if(i < current.length() && current.length() >= i+2 && current.substring(i,i+2).equals("/*") && quoteFlag == true)
                    {
                        fb = false;
                    }
                    
                    if(counter == 2)
                    {
                        quoteFlag = true;
                        counter = 0;
                    }
                        
                    
                    if(i < current.length() && current.charAt(i) == '"' && fb == true)
                    {
                        if(current.charAt(i-1) != '\\')
                                counter++;
                        quoteFlag = false;
                    }

                    if(current.length() >= i+2 && current.substring(i,i+2).equals("//") && bc == true && quoteFlag == true)
                    {
                        break;
                    }
                    
                    if(i < current.length() &&fb == true && quoteFlag == true && bc == true)
                    {
                        if(current.charAt(i) == '{')
                        {
                            stack.push("{");
                        }
                        
                        if(current.charAt(i) == '}')
                        {
                            stack.pop();
                            if(stack.empty() == true)
                            {
                                return lineCount;
                            }
                        }
                    }                   
                }
                }
            }
            lineCount++;
        }
        return lineCount;
    }

    /**
     * Searches the file for occurences of newMethod, if there is occurences then
     * the refactoring is not possible
     * @param newMethod
     * @param file
     * @return
     */
    public boolean searchNewValue(String newMethod, String file)
    {
        Vector<String> comment = new Vector<String>();
        comment.add("");
        comment.add("*");
        comment.add("**");
        comment.add("***");
        comment.add("****");
        comment.add("*****");
        comment.add("******");
        boolean value = false;
        int i = 0;
        Scanner scanner = new Scanner(file);
        Vector<String> characterList = characters.characterListMethod();
        while(scanner.hasNext())
        {
            String currentLine = scanner.nextLine();
            if(currentLine.contains("*/"))
            {
                String sub = currentLine.substring(currentLine.indexOf("*/"));
                int length = currentLine.substring(currentLine.indexOf("*/")).length();
                if(length != 0)
                {
                    for(int l = 0; l < characterList.size(); l++)
                    {
                        for(int q = 0; q < characterList.size(); q++)
                        {
                            if(sub.contains(characterList.get(l) + newMethod + characterList.get(q)))
                            {
                                value = true;
                                q = characterList.size();
                                l = characterList.size();
                                return value;
                            }
                            else
                            {

                            }
                        }
                    }
                }   
            }
            else if(currentLine.contains("/*"))
            {
                for(int t = 0; t < comment.size(); t++)
                {
                    if(currentLine.contains("/*"+comment.get(t)))
                    {
                        if(currentLine.contains("*/"))
                        {
                            t = comment.size();
                        }
                        else
                        {
                            String current = scanner.nextLine();
                            while(!current.contains("*/"))
                            {
                                current = scanner.nextLine();
                            }
                            t = comment.size();
                        }
                    }
                }
            }
            else if(currentLine.equals("//"))
            {
                
            }
            else if(currentLine.contains(newMethod))
            {
                for(int l = 0; l < characterList.size(); l++)
                {
                    for(int q = 0; q < characterList.size(); q++)
                    {
                        if(currentLine.contains(characterList.get(l) + newMethod + characterList.get(q)))
                        {
                            value = true;
                            q = characterList.size();
                            l = characterList.size();
                            return value;
                        }
                        else
                        {

                        }
                    }
                }
            }
            else
            {
                
            }
            i++;
        }
        //returns true if there is a methodClash, which means no refactoring is possible
        return value;
    }
    
    /**
     * Searches the file for occurences of newMethod, if there is occurences then
     * the refactoring is not possible
     * @param newMethod
     * @param file
     * @return
     */
    public boolean searchNewValue_Only_In_Method(String newMethod, String file)
    {
        Scanner scanner = new Scanner(file);
        boolean result = false;
        int i = 1;
        while(scanner.hasNext())
        {
            scanner.nextLine();
            if(i == methodLineNumber)
            {
                for(int k = i; k < endOfMethod; k++)
                {
                    String currentLine = scanner.nextLine();
                    if(currentLine.contains(newMethod))
                    {
                        result = true;
                        return true;
                    }
                }
            }
            i++;
        }
        return result;
    }
    
    /**
     * Returns a description of the current refactoring method
     * @return 
     */
    @Override
    public String description() 
    {
        //Returns the description of Introduce local extension
        return "Renames a parameter and every value in that function.";
    }    
}